package com.myCode.thread;

import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

/**
 * 多线程之间的通信
 * 实现多个线程之间的协同，如：线程执行先后顺序、获取某个线程执行的结果等等。
 * 涉及到线程之间互相通信，分为下面四类：
 * 1> 文件共享
 * 2> 网络共享
 * 3> 共享变量
 * 4> JDK提供的线程协调 API
 *    suspend/resume:已经被弃用
 *    wait/notify
 *    park/unpark
 *
 * 伪唤醒：之前的代码中用if语句来判断，是否进入等待状态，是错误的。
 * 官方建议应该在循环中检查等待条件，原因是处于等待状态的线程可能会收到错误警报和伪唤醒，如果不在循环中检查等待条件，程序就会在没有满足结束条件的情况下退出
 * 伪唤醒：涉及到虚拟机底层、CPU调度等，
 * 伪唤醒：指线程并非因为 notify、notifyAll、unpark等 api 调用而唤醒，是更底层原因导致的
 */
public class ThreadMessage {

    private static String context = "";

    private static Object object = null;

    /**
     * 变量共享
     */
    public void shareVariable(){

        // 线程一：写入线程
        new Thread(()->{
            try {
                while (true){
                    context = "当前时间" + String.valueOf(System.currentTimeMillis());
                    Thread.sleep(1000L);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            try {
                while (true){
                    Thread.sleep(1000L);
                    System.out.println(context);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

    public void productConsume(){

    }

    /**
     * 容易产生死锁
     *
     * resume() 后面使用 suspend() 也会产生死锁
     * @throws InterruptedException
     */
    public void suspendResumeTest() throws InterruptedException {
        Thread thread = new Thread(() -> {
            if (Objects.isNull(object)) {
                System.out.println("没有产品，进入等待");
                Thread.currentThread().suspend();
            }
            System.out.println("买到产品，赶紧使用");
        });
        thread.start();
        Thread.sleep(3000L);
        object = new Object();
        thread.resume();
        System.out.println("通知产品采买线程");
    }

    /**
     * wait、notify机制：
     * 只能由同一对象锁的持有者线程调用，也就是写在同步块里面，否则会抛出 IllegalMonitorStateException异常
     * wait方法导致当前线程等待，加入该对象的等待集合中，并且放弃当前持有对象锁。
     * notify/notifAll方法唤醒一个或者所有正在等待这个对象锁的线程
     * 注意：虽然 wait 自动解锁，但是对顺序有要求，如果在 notify 被调用之后，才开始 wait 方法的调用，线程会永远处于 WAITING 状态
     */
    public void waitNotify() throws InterruptedException {

        new Thread(() -> {
            if (Objects.isNull(object)){
                // 执行这块代码，让 wait 在 notify 之后执行，会让程序死锁
                // try {
                //     Thread.sleep(5000L);
                // } catch (InterruptedException e) {
                //     e.printStackTrace();
                // }
                synchronized (this){
                    try {
                        System.out.println("1、进入等待");
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("2、买到产品回家");
        }).start();

        Thread.sleep(3000L);
        object = new Object();
        synchronized (this){
            this.notifyAll();
            System.out.println("3、通知消费者");
        }

    }

    /**
     * 线程调用 park 则等待 许可，unpark方法为指定线程提供 许可
     * 不要求 park和 unpark 方法的调用顺序。
     * 多次调用 unpark 之后，再调用 park，线程会直接运行。但不会叠加，连续多次调用 park 方法，第一次会拿到 许可 直接运行，后续调用会进入等待
     * park 和 unpark 在同步代码块中，是不是释放拥有的锁的，在同步代码块中使用 park、unpark容易造成死锁
     */
    public void parkUnpark() throws InterruptedException {
        Thread thread = new Thread(() -> {
            if (Objects.isNull(object)) {
                System.out.println("1、没有产品，进入等待");
                // 等待许可
                // LockSupport.park();
                synchronized (this){
                    LockSupport.park();
                }
            }
            System.out.println("2、获得产品，赶紧运行");
        });
        thread.start();
        Thread.sleep(3000L);
        object = new Object();
        // 提供许可
        System.out.println("3、生产了产品，通知消费者");
        synchronized (this){
            LockSupport.unpark(thread);
        }
        // LockSupport.unpark(thread);
    }
}
